/*  minesweeper board object for ND++ minesweeeper game
Programmer:  Prof. Scott

This class contains implementationf of member functioned used in the  main object of our initial minesweeper game

Cells will be constructed using new and stored in a 2D vector.  We'll also store the number of rows, columns and
nonBomb cells.  We don't care about how many bombs there are in this particular implementation but one could.

Please refer to board.cpp for additional details

*/

#include <QMessageBox>
#include <QTimer>
#include <QtGui>
#include <iostream>
#include <vector>
#include <cstdlib>
#include <ctime>
#include "Board.h"   
#include "quadrant.h"   // include both derived classes we will use
#include "bomb.h"
 
//#define DEBUG    // turn debugging on for now
//#define TERMINAL_OUT
 
using namespace std;

// non-default constructor; rows, cols and number of bombs default to 10 (100 cell, 90 non-bomb cells)
Board::Board(QWidget* parent, int rows, int cols, int numBombs, int width, int height) : QWidget(parent) {
	setWindowTitle(tr("Welcome to Minesweeper"));
	move(0, 0); // puts in top left of screen
	screen_width = width;
	screen_height = height;
	resize(screen_width, screen_height); // defined in main.cpp
	raise(); // brings to front

	board_rows = rows;   // save # of rows and cols
	board_cols = cols;
	bombCount = numBombs;

	// memory allocation using new
#ifdef DEBUG
	cout << "Initializing a board with " << rows << " rows and " << cols << " columns." << endl;
#endif

	srand((unsigned)time(0));  // initialize random number generator
	resetBoard();

	QMessageBox instructions(QMessageBox::Information, "Welcome to Minesweeper!", "Click a square to test it. Right click to flag it.");
	instructions.addButton("Ok", QMessageBox::AcceptRole);
	instructions.raise();
	instructions.show();
	instructions.exec();
}

void Board::resetBoard() {
	showbombs = 0;
	grid.clear();
	for (int i = 0; i < board_rows; i++) {
		vector<Cell*> temp;	
		for (int j = 0; j < board_cols; j++) {
			temp.push_back (new Quadrant);      // add new quadrants; note base class pointer can easily point to derived class
		}
		grid.push_back(temp);
	}

	int randi, randj;
	nonBombCells = board_rows * board_cols - bombCount;   // compute "clear" cells
	
	for (int bombs = 0; bombs < bombCount;) {
		randi = board_rows*(rand()/(RAND_MAX + 1.0));   // compute random i,j coordinates
		randj = board_cols*(rand()/(RAND_MAX + 1.0));
    
		if (grid[randi][randj]->checkIfBomb() == 0) {   // if this is not already a bomb
#ifdef DEBUG
			cout << "Putting a bomb at: " << randi << ", " << randj << endl;
#endif
			delete grid[randi][randj];       // to avoid a member leak we delete/deconstruct the quadrant here
			grid[randi][randj] = new Bomb;   // place the bomb
			bombs++;                         // increment number of bombs successfully placed
		}
	}
}

// printBoard private utility function; uses polymorphism and virtual functions
void Board::printBoard() {
	update(); // displays graphically
#ifdef TERMINAL_OUT
	// debugging info for the programmer, although some games may report this to the user
#ifdef DEBUG
	cout << "Non-Bomb cells remaining: " << nonBombCells << endl;
#endif

	// display board header
	cout << "a| b- ";
	for (int j = 0; j < board_cols; j++)
		cout << j << " ";
		cout << endl;

		// display board
		for (int i = 0; i < board_rows; i++) {
			cout << i << ":    ";
			for (int j = 0; j < board_cols; j++)
				grid[i][j] -> display();             // call either bomb display or quadrant display using a base class pointer

			cout << endl;
		}
#endif
}

// plays a minesweeper game by getting input from the user
void Board::play(QMouseEvent* e) { 
	int a, b, choice;    // user-provided input

	a = (e->y())/(screen_width/board_rows);
	b = (e->x())/(screen_height/board_cols);
    int flag = grid[a][b]->checkIfFlagged();  // is this cell flagged?
	if(flag == 0) {
		if(e->button() == Qt::LeftButton) choice = 0;
		else choice = 1;
	}
    else cout << "Removing the flag from " << a << "  " << b << endl;  // flagged, so remove it automatically for user

    if (choice == 1 || flag == 1) {      
		grid[a][b]->flipFlag();    // flip flag either by request or automattically
    }
    else {
		if (grid[a][b]->checkIfBomb() == 1){
			showbombs = true;
			printBoard();
			QMessageBox gameover(QMessageBox::Information, "BOOM!", "Game Over!");
			QAbstractButton* newgamebutton = gameover.addButton("New Game", QMessageBox::YesRole);
			QAbstractButton* quitbutton = gameover.addButton("Quit", QMessageBox::NoRole);
			gameover.show();
			gameover.exec();
			
			if(gameover.clickedButton() == newgamebutton) resetBoard(); // new game
			else if(gameover.clickedButton() == quitbutton) exit(0); // quits
		}
		else bombSweep(a, b); 
		// this isn't a bomb, so sweep it if necessary.  Could be more efficient if we
		// checked if this was defined already, but this is done also in this function
    }
  
  // if nonBombCells == 0, this means the user has uncovered all the non-bomb cells and therefore has won!
	if (nonBombCells == 0) {
		printBoard();   // display current board
		QMessageBox winner(QMessageBox::Information, "Congrats!", "You Won!");
		QAbstractButton* newgamebutton = winner.addButton("New Game", QMessageBox::YesRole);
		QAbstractButton* quitbutton = winner.addButton("Quit", QMessageBox::NoRole);
		winner.show();
		winner.exec();
		
		if(winner.clickedButton() == newgamebutton) resetBoard(); // new game
		else if(winner.clickedButton() == quitbutton) exit(0); // quits
	}
	printBoard();   // display current board
}

void Board::bombSweep(int x, int y) {
  // check to make sure we are not off the board on the bottom or the right
  if (x >= board_rows || y >= board_cols)
    return;

  // continue only if the number of neighbors for this cell is unknown
  if (grid[x][y] -> getNeighbors() == -1) {

    int neighbors = 0;  // current number of known neighbors
    int xpos, ypos;    // coordinates of a calculated neighbor

    for (int i = -1; i <= 1; i++) {  // upper left diagonal cell is [xcoord-1][ycoord-1];  lower right diag is [xcoord+1][ycoord+1]

      // compute x coordinate of subgrid
      xpos = x + i;
      
      // if xpos is off the board (x == 0 and i == -1), set i to zero and update xpos (which is 0) 
      if (xpos < 0)
	xpos = i = 0;

      for (int j = -1; j <= 1; j++) {   // look at y coords the same way as x

	// compute x coordinate of subgrid
	ypos = y + j;

	// if ypos is off the board (y == 0 and j == -1), set j to zero and update ypos (which is also 0) 
	if (ypos < 0)
	  ypos = j = 0;

	// make sure we are not off board; we don't have to ignore the current cell as we know checkIfBomb will be zero
	if (xpos < board_rows && ypos < board_cols) {

	  if (grid[xpos][ypos]->checkIfBomb() == 1)   // bomb!  update neighbors
	    neighbors++;
	
	}
    
      }

    }

    // player successfully avoided a bomb; update nonBombcells remaining and update neighbors for this cell
    nonBombCells--;
    grid[x][y]->setNeighbors(neighbors);

    if (neighbors == 0) {

      for (int i = -1; i <= 1; i++) {

	// calculate xpos the same as above
	xpos = x + i;
	if (xpos < 0)
	  xpos = i = 0;

	for (int j = -1; j <= 1; j++) {

	  // calculate ypos the same as above
	  ypos = y + j; 
	  if (ypos < 0) 
	    ypos = j = 0;
	  
	  // sweep neighbor if it is not this cell (i.e, xpos, ypos is not x,y)
	  if (xpos != x || ypos != y)	  
	      bombSweep(xpos, ypos);
	  
	}

      }
      
    }
    
  }

}

void Board::paintEvent(QPaintEvent*) {
	QPainter painter(this); // painter object to draw stuff

	painter.setPen(Qt::white); // outline
	painter.setBrush(Qt::white); // fill
	
	double rsize = (double)screen_width/board_rows;
	double csize = (double)screen_height/board_cols;
	for(int r = 0; r < board_rows; r++) {
		for(int c = 0; c < board_cols; c++) {
			if(grid[c][r]->checkIfBomb() == 1 && showbombs) {
				painter.setBrush(Qt::blue);
				painter.drawRect(r*rsize, c*csize, rsize, csize);
			}
			else if(grid[c][r]->checkIfFlagged()) {
				painter.setBrush(Qt::yellow);
				painter.drawRect(r*rsize, c*csize, rsize, csize);
			} // flagged square
			else if(grid[c][r]->getNeighbors() >= 0) {
				if(grid[c][r]->getNeighbors() == 0) painter.setBrush(Qt::white);
				else painter.setBrush(Qt::red);
				painter.drawRect(r*rsize, c*rsize, rsize, csize);
				painter.drawText(r*rsize + rsize/2, c*csize + csize/2, QString("%1").arg(grid[c][r]->getNeighbors()));
			} // has been clicked
			else {
				painter.setBrush(Qt::black);
				painter.drawRect(r*rsize, c*csize, rsize, csize);
			}
		}
	}
	
} // called when screen is updated

void Board::mousePressEvent(QMouseEvent* e) {
	play(e);
} // called when mosue is clicked
